#include <iostream>
#include <vector>
#include <algorithm>

// 使用 long long 防止整数溢出，因为路径和可能很大
using ll = long long;

// 定义一个非常小的数作为负无穷大，用于初始化dp数组
// 路径和可能为负，所以不能用0初始化
const ll INF = -1e18; // 1000*1000*10000 = 10^10，ll可以存下

int main() {
    // 提高C++ IO效率
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    int n, m;
    std::cin >> n >> m;

    std::vector<std::vector<int>> a(n + 1, std::vector<int>(m + 1));
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            std::cin >> a[i][j];
        }
    }

    // dp[i][j][k] 表示到达格子 (i, j) 的最大路径和
    // k = 0: 最后一步是从左边 (i, j-1) 来的
    // k = 1: 最后一步是从上面 (i-1, j) 来的 (向下移动)
    // k = 2: 最后一步是从下面 (i+1, j) 来的 (向上移动)
    std::vector<std::vector<std::vector<ll>>> dp(n + 2, std::vector<std::vector<ll>>(m + 2, std::vector<ll>(3, INF)));

    // 起点 (1, 1) 初始化
    // 到达 (1, 1) 本身，路径和就是 a[1][1]
    // 逻辑上可以认为是从 (1, 0) 这个虚拟格子过来的
    dp[1][1][0] = a[1][1];
    
    // DP过程：逐列计算
    for (int j = 1; j <= m; ++j) {
        // Step 1: 从左边 (j-1) 列转移到当前 (j) 列
        if (j > 1) {
            for (int i = 1; i <= n; ++i) {
                // 从 (i, j-1) 点可以向右到达 (i, j)
                // (i, j-1) 可能是从左/上/下三个方向到达的，取其中最大值
                ll from_left = std::max({dp[i][j-1][0], dp[i][j-1][1], dp[i][j-1][2]});
                // 如果前一格的所有状态都不可达，则当前状态也无法通过此路径到达
                if (from_left != INF) {
                    dp[i][j][0] = from_left + a[i][j];
                }
            }
        }

        // Step 2: 在当前列 j 内，进行向上/向下的更新
        // 从上往下更新
        for (int i = 2; i <= n; ++i) {
            // 要到达 (i, j) 且最后一步是向下，必须从 (i-1, j) 过来
            // 在 (i-1, j)，不能是从 (i, j) 往下再往上，所以不考虑 dp[i-1][j][2]
            ll from_up = std::max(dp[i-1][j][0], dp[i-1][j][1]);
            if (from_up != INF) {
                // 当前状态 dp[i][j][1] 可能已经被之前的计算更新过（或者没有）
                // 需要和从 (i-1,j) 走过来的新路径和比较，取最大值
                dp[i][j][1] = std::max(dp[i][j][1], from_up + a[i][j]);
            }
        }

        // 从下往上更新
        for (int i = n - 1; i >= 1; --i) {
            // 要到达 (i, j) 且最后一步是向上，必须从 (i+1, j) 过来
            // 在 (i+1, j)，不能是从 (i, j) 往上再往下，所以不考虑 dp[i+1][j][1]
            ll from_down = std::max(dp[i+1][j][0], dp[i+1][j][2]);
            if (from_down != INF) {
                dp[i][j][2] = std::max(dp[i][j][2], from_down + a[i][j]);
            }
        }
    }

    // 终点是 (n, m)。到达该点的路径可能是从左边 (n, m-1) 或上面 (n-1, m) 来的。
    // 所以取 dp[n][m][0] 和 dp[n][m][1] 的较大值。
    // dp[n][m][2] 是从 (n+1, m) 来的，不合法，其值应为INF。
    ll ans = std::max(dp[n][m][0], dp[n][m][1]);
    
    // 对于只有一个格子的特殊情况(1x1)
    if (n == 1 && m == 1) {
        ans = a[1][1];
    } else if (m == 1) { // 只有一列，只能往下走
        // 只有一列时，没有从左边来的转移，dp[i][1][0]只在i=1时有值
        // dp[i][1][1] 是从上往下累加的
        ans = dp[n][1][1];
    }
    
    std::cout << ans << std::endl;

    return 0;
}
